////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek Studios, 2001-2010.
// -------------------------------------------------------------------------
//  File name:   Statoscope.h
// -------------------------------------------------------------------------
//
////////////////////////////////////////////////////////////////////////////

#ifndef __Statoscope_h__
#define __Statoscope_h__
#pragma once

#include "IStatoscope.h"

#if ENABLE_STATOSCOPE

#include "IParticles.h"

#include "MemReplay.h"
#include "STLGlobalAllocator.h"

struct SPerfStatFrameProfilerRecord
{
	CFrameProfiler *m_pProfiler;
	int m_count;
	float m_selfTime;
};

struct SPerfTexturePoolAllocationRecord
{
	int m_size;
	string m_path;
};

enum EElementDataType
{
	DATATYPE_INT,
	DATATYPE_FLOAT,
	DATATYPE_STRING,
	DATATYPE_PATH
};

struct SDataElement
{
	SDataElement(int i) : m_i(i), m_dataType(DATATYPE_INT) {}
	SDataElement(uint32 i) : m_i(i), m_dataType(DATATYPE_INT) {}
	SDataElement(float f) : m_f(f), m_dataType(DATATYPE_FLOAT) {}
	SDataElement(const char *s) : m_s(s), m_dataType(DATATYPE_STRING) {}
	SDataElement(void *p) : m_p(p), m_dataType(DATATYPE_PATH) {}

	union
	{
		int m_i;
		float m_f;
		const char *m_s;
		void *m_p;
	};

	EElementDataType m_dataType;
};

struct SCallstack
{
	string m_tag;
	std::vector<void*> m_addresses;

	SCallstack() {}

	SCallstack(void** addresses, uint32 numAddresses, const char *tag)
		: m_addresses(addresses, addresses + numAddresses), m_tag(tag)
	{}

	void swap(SCallstack &other)
	{
		m_tag.swap(other.m_tag);
		m_addresses.swap(other.m_addresses);
	}
};

struct SPerfStatFrameRecord
{
	float m_frameTimeInS;
	uint8 *m_pScreenshotBuf;
	std::vector<SDataElement> m_values;
	uint32 m_curIdx;

	// some of this data doesn't need to be stored in here as most things are copied into m_values - will sort out later :)
	uint32 m_drawProfilerRecordCount;
	std::vector<SPerfStatFrameProfilerRecord> m_frameProfilerRecords;
	std::vector<SPerfTexturePoolAllocationRecord> m_texturePoolAllocationRecords;
	std::vector<string> m_userMarkers;
	std::vector<SCallstack> m_callstacks;

	SPerfStatFrameRecord()
	{
		m_frameTimeInS = -1.0f;
		m_pScreenshotBuf = NULL;
		m_curIdx = 0;
		m_drawProfilerRecordCount = 0;
	}

	void AddValue(SDataElement dataElement)
	{
		m_values.push_back(dataElement);
	}

	const SDataElement& GetNextValue()
	{
		return m_values[m_curIdx++];
	}
};

struct SDataGroup
{
	virtual ~SDataGroup(){}
	SDataGroup(char id, const char *name, const char *format, uint32 numDataElements) :
		m_id(id), m_name(name), m_format(format), m_numDataElements(numDataElements) {}

	virtual void AddDataToFrameRecord(SPerfStatFrameRecord &fr) = 0;
	virtual uint32 GetNumDataSets(const SPerfStatFrameRecord &fr) { return 1; }
	virtual string GetPath(void *p) { return NULL; }

	const char m_id;
	const char *m_name;
	const char *m_format;
	uint32 m_numDataElements;
};

class ReplaySocketWriter;

// Statoscope implementation, access IStatoscope through gEnv->pStatoscope
class CStatoscope : public IStatoscope, public ISystemEventListener, ICaptureFrameListener
{
public:
	CStatoscope();
	~CStatoscope();

	VIRTUAL void Tick();
	VIRTUAL void AddUserMarker(const char *name);
	VIRTUAL void LogCallstack(const char *tag);
	VIRTUAL void LogCallstackFormat(const char *tagFormat, ...);
	VIRTUAL void SetCurrentProfilerRecords(const std::vector<CFrameProfiler*> *profilers);
	VIRTUAL void SetCurrentTexturePoolAllocations(std::vector<STexturePoolAllocation*> *allocations);
	VIRTUAL void DumpFrameRecords(bool bOutputFormat = false);
	VIRTUAL bool NeedsGPUTimers();

	// ISystemEventListener
	VIRTUAL void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);

protected:
	enum EScreenShotCaptureState
	{
		eSSCS_Idle = 0,
		eSSCS_RequestCapture,
		eSSCS_AwaitingBufferRequest,
		eSSCS_AwaitingCapture,
		eSSCS_DataReceived
	};

	enum { SCREENSHOT_SCALING_FACTOR = 8 };

	typedef int (*LineWriterFunc)(void*, const char*, ...);

	void AddFrameRecord();
	void SetLogFilename();
	void SetDataGroups();
	void OutputLoadedModuleInformation(LineWriterFunc lineWriter, void* lineWriterInst);
	void StoreCallstack(const char* tag, void** callstack, uint32 callstackLength);

	const char* GetDataGroupsCVarHelpString();
	static void ConsoleAddUserMarker(IConsoleCmdArgs* pParams);

	//Screenshot capturing
	virtual unsigned char *OnNeedFrameData(void);
	virtual uint32 OnNeedFrameDataRSXOffset(void);
	virtual void OnFrameCaptured(void);
	virtual int OnGetFrameWidth(void);
	virtual int OnGetFrameHeight(void);
	virtual int OnGetCaptureFlags(void);
	virtual int OnCaptureFrameBegin(void);
	
	//Setup statoscope for fps captures	
	virtual void SetupFPSCaptureCVars();

	//Request statoscope screen shot, may not succeed if screem shot is already in progress
	virtual bool RequestScreenShot();

	void PrepareScreenShot();
	uint8* ProcessScreenShot();

	std::vector<SPerfStatFrameRecord> m_perfStatFrameRecords;
	CryMT::vector<string> m_userMarkers;
	CryMT::vector<SCallstack> m_callstacks;
	std::vector<SDataGroup*> m_activeDataGroups;
	std::vector<SDataGroup*> m_allDataGroups;

	ICVar *m_pStatoscopeDumpPeriodCVar;
	ICVar *m_pStatoscopeDumpManualCVar;
	ICVar *m_pStatoscopeDumpAllCVar;
	ICVar *m_pStatoscopeDataGroupsCVar;
	ICVar *m_pStatoscopeScreenshotCapturePeriodCVar;
	ICVar *m_pStatoscopeFilenameUseBuildInfoCVar;
	ICVar *m_pStatoscopeFilenameUseMapCVar;
	ICVar *m_pStatoscopeFilenameUseTagCvar;
	ICVar *m_pStatoscopeFilenameUseTimeCVar;
	ICVar *m_pStatoscopeMinFuncLengthMsCVar;
	ICVar *m_pStatoscopeMaxNumFuncsPerFrameCVar;
	ICVar *m_pStatoscopeCreateLogFilePerLevelCVar;
	ICVar* m_pStatoscopeSocketAddress;
	ICVar *m_pTimerSmoothingCVar;
	ICVar *m_pGameRulesCVar;
	ICVar *m_pStatoscopeAllowFPSOverrideCVar;

	string m_currentMap;

	float m_lastDumpTime;
	float m_screenshotLastCaptureTime;
	bool m_groupMaskInitialized;
	bool m_bCreateNewLog;
	bool m_bLogFileCreated;
	bool m_bHaveOutputModuleInformation;
	uint32 m_activeDataGroupMask;
	uint32 m_logNum;

	std::vector<SPerfStatFrameProfilerRecord> m_frameProfilerRecords;	// the most recent frame's profiler data
	std::vector<CFrameProfiler*> m_perfStatDumpProfilers;	// only used locally in SetCurrentProfilerRecords() but kept to avoid reallocation
	
	std::vector<SPerfTexturePoolAllocationRecord> m_texturePoolAllocationRecords;

	string m_logFilename;

	uint8 *m_pScreenShotBuffer;	//Buffer for the render thread to asynchronously push screenshots to
	volatile EScreenShotCaptureState m_ScreenShotState;
	uint32 m_pScreenShotRSXOffset;				//Offset for mapping m_pScreenShotBuffer to RSX so that the DXPS device can push screenshots to it asynchronously

	ReplaySocketWriter* m_socketWriter;
	std::vector<char, stl::STLGlobalAllocator<char> > m_socketBuffer;
	size_t m_socketBufferEnd;

private:
	static int WriteLineSocket(void* inst, const char* format, ...);

private:
	CStatoscope(const CStatoscope&);
	CStatoscope& operator = (const CStatoscope&);
};

#endif // ENABLE_STATOSCOPE

#endif  // __Statoscope_h__
